/**
 * 将HTML文本字符串解析成树状的Tag对象
 * **/
const tagReg = /<([a-zA-Z1-9]+)([^>]*)>|<\/([a-zA-Z1-9]+)([^<]*)>/gm;

export function parseHTML(html = '') {
  let stack = [];
  let result = tagReg.exec(html);
  let nodes = [];
  let currentIdx = 0;

  while (result) {
    const openTagName = result[1] || '';
    let propStr = result[2] || '';
    const isSelfClosingTag = openTagName && propStr.slice(-1) === '/';
    const endTagName = result[3] || '';

    if (isSelfClosingTag) {
      propStr = propStr.slice(0, -1);
    }

    // 处理位于新匹配的标签之前的文本
    if (result.index >= currentIdx) {
      const content = html.slice(currentIdx, result.index);
      const textTag = new Tag({
        startIndex: currentIdx,
        endIndex: result.index,
        content
      });

      // 如果是开标签
      if (openTagName) {
        if (content) {
          pushChild(stack, textTag, nodes);
        }
        // 更新currentIdx
        currentIdx = result.index + result[0].length;
      } else if (endTagName) {
        // 如果是闭标签，加入该标签的children
        if (content) {
          pushChild(stack, textTag);
        }
        // 更新currentIdx
        currentIdx = result.index + result[0].length;
      }
    }

    // 如果是开标签
    if (openTagName) {
      const openTag = new Tag({
        tagName: openTagName,
        startIndex: result.index,
        propStr
      });

      // 如果是自闭合标签，将标签加入父
      if (isSelfClosingTag) {
        openTag.endIndex = result.index + result[0].length;
        pushChild(stack, openTag, nodes);
      } else {
        // 将标签推入栈
        stack.push(openTag);
      }
    } else if (endTagName) {
      // 如果是闭标签
      // 匹配栈中的开标签
      if (stack.length > 0) {
        const lastTag = getLastOne(stack);
        // 标签匹配成功
        if (endTagName === lastTag.tagName) {
          stack.pop();

          lastTag.endIndex = result.index + result[0].length;

          pushChild(stack, lastTag, nodes);
        }
      }
    }

    result = tagReg.exec(html);
  }

  if (currentIdx < html.length) {
    nodes.push(
      new Tag({
        startIndex: currentIdx,
        endIndex: html.length - 1,
        content: html.slice(currentIdx)
      })
    );
  }

  if (nodes.length > 0) {
    return nodes;
  } else {
    return [
      new Tag({
        startIndex: 0,
        endIndex: html.length - 1,
        content: html
      })
    ];
  }
}

function getLastOne(arr = []) {
  return arr.length > 0 ? arr[arr.length - 1] : null;
}

function Tag({ tagName, startIndex, endIndex, propStr, content, children }) {
  let tag = {
    tagName: tagName || '',
    startIndex: startIndex || 0,
    endIndex: endIndex || 0
  };
  if (tagName) {
    tag.children = children || [];
    tag.propStr = propStr || '';
  } else {
    content = replaceESC(content);
    tag.content = content || '';
  }
  return tag;
}

function pushChild(stack = [], child = {}, nodes) {
  let parentTag = getLastOne(stack);
  if (parentTag) {
    parentTag.children.push(child);
  } else if (nodes) {
    nodes.push(child);
  }
}

export function decodeHTMLEntities(str = '') {
  return (
    String(str)
      // .replace(escReg, (all, esc) => compileESC(esc))
      .replace(/[\r\n\u21b5]/gm, '')
  );
}

/**
 * 将String的转义字符转换成浏览器解析的字符
 * **/
const escReg = /(&(?:#)?[a-zA-Z0-9]{2,6};)/gm;

export function compileESC(esc = '') {
  if (!compileESC.dom) {
    compileESC.dom = document.createElement('div');
  }

  compileESC.dom.innerHTML = esc;
  return compileESC.dom.innerText || compileESC.dom.textContent;
}

// 替換转义字符串
export function replaceESC(str = '') {
  return str.replace(escReg, (all, esc) => compileESC(esc));
}

/**
 * 将html标签上的属性字符串转成对象
 * **/
const propsReg = /([A-Za-z0-9\-]+)(?:=(['|"])([^\2]*?)\2)?/gm;

export function formatProps(propsStr = '') {
  let props = {};
  propsStr = propsStr.trim();
  let result = propsReg.exec(propsStr);

  while (result) {
    let key = '',
      value = '';

    key = result[1] || '';
    value = result[3];
    if (value === undefined) {
      value = true;
    }

    // 对React不支持的prop进行过滤
    if (key && !FILTER_PROPS.includes(key)) {
      // key值转换处理
      key = htmlProp2ReactProp(key);
      // key值驼峰化处理
      key = camelBakStr(key);
      // style的value值转换处理
      if (key === 'style') {
        if (typeof value !== 'string') {
          value = '';
        }
        value = formatStyle2JSON(value);
      }
      props[key] = value;
    }

    result = propsReg.exec(propsStr);
  }

  return props;
}

const REACT_PROPS_CONFIG = {
  class: 'class',
  for: 'htmlFor',
  tabindex: 'tabIndex',
  spellcheck: 'spellCheck',
  viewbox: 'viewBox',
  attributename: 'attributeName',
  frameborder: 'frameBorder',
  allowfullscreen: 'allowFullScreen',
  marginheight: 'marginHeight',
  colspan: 'colSpan',
  rowspan: 'rowSpan'
};

const FILTER_PROPS = ['valign', 'powered-by', 'contenteditable'];

/**
 * 将html的标签属性转换成React约定的属性名称
 * **/
function htmlProp2ReactProp(propName = '') {
  return REACT_PROPS_CONFIG[propName] || propName;
}

/**
 * 将html标签中用'-'相连的属性字符串转换成驼峰形式
 * **/
const camelBakReg = /-(\w)/gm;

function camelBakStr(str = '') {
  if (str.includes('data-')) {
    return str;
  } else {
    return str.replace(camelBakReg, (all, letter) => {
      return letter.toUpperCase();
    });
  }
}

/**
 * 将style的内联样式转化为JSX语法的JSON对象
 * **/
const styleReg = /([A-Za-z0-9\-]+)(?::([^;]+))/gm;

function formatStyle2JSON(styleStr = '') {
  let styleJSON = {};
  styleStr = replaceESC(styleStr);
  let result = styleReg.exec(styleStr);
  while (result) {
    let key = '',
      value = '';

    key = result[1] || '';
    value = result[2] || true;

    if (key) {
      key = camelBakStr(key);
      styleJSON[key] = value.trim();
    }

    result = styleReg.exec(styleStr);
  }
  return styleJSON;
}
